//
//  NetworkClient.m
//  NetworkDemo
//
//  Created by 万孟超 on 2021/9/7.
//

#import "NetworkClient.h"

@interface NetworkClient()
/*
 *存储正在进行的网络请求
 *key:网络请求引擎的指针地址，value为对应的网络请求任务
 */
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSURLSessionDataTask *> *engineTasks;
/*
 已完成的网络请求
 存放的是engine的指针地址｜方法名
 */
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *completeTask;
/*
 *存储需要重试的请求
 */
@property (nonatomic, strong) NSMutableArray<NetworkRetryModel *> *retryTasks;
/*
 *存储请求超时需要重试的请求
 */
@property (nonatomic, strong) NSMutableArray<NetworkRetryModel *> *timeoutRetryTasks;

@end

@implementation NetworkClient

/**
 单例初始化方法
 */
+ (instancetype)sharedManager {
    static dispatch_once_t onceToken;
    static id _sharedManager = nil;
    dispatch_once(&onceToken, ^{
        _sharedManager = [[self alloc] init];
        //注册重新请求通知
        [[NSNotificationCenter defaultCenter] addObserver:_sharedManager selector:@selector(startRetryNetwork) name:@"NETWORK_RETRY" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:_sharedManager selector:@selector(startTimeoutRetryNetwork) name:@"NETWORK_TIMEOUT_RETRY" object:nil];
    });
    return _sharedManager;
}

/// 开始重试网络请求
- (void)startRetryNetwork {
    NSArray<NetworkRetryModel *> *tempRetryArray = [NSArray arrayWithArray:self.retryTasks];
    [self.retryTasks removeAllObjects];
    [tempRetryArray enumerateObjectsUsingBlock:^(NetworkRetryModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj retryNetwork];
    }];
}

/// 开始重试网络请求
- (void)startTimeoutRetryNetwork {
    NSArray<NetworkRetryModel *> *tempRetryArray = [NSArray arrayWithArray:self.timeoutRetryTasks];
    [self.timeoutRetryTasks removeAllObjects];
    [tempRetryArray enumerateObjectsUsingBlock:^(NetworkRetryModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj retryNetwork];
    }];
}

/// 记录请求任务
/// @param engineTask 请求引擎任务
/// @param enginePoint 引擎内存地址
- (void)setEngineTask:(NSURLSessionDataTask *)engineTask forEnginePoint:(NSString *)enginePoint {
    if (enginePoint) {
        [self.engineTasks setValue:engineTask forKey:enginePoint];
        NSLog(@"记录请求:%@",enginePoint);
    }
}

/// 记录需要重试的请求任务
/// @param retryModel 重试的Model
- (void)addRetryModel:(NetworkRetryModel *)retryModel {
    [self.retryTasks addObject:retryModel];
    NSLog(@"记录到重试列表");
}

/// 记录请求超时需要重试的请求任务
/// @param retryModel 重试的Model
- (void)addTimeoutRetryModel:(NetworkRetryModel *)retryModel {
    [self.timeoutRetryTasks addObject:retryModel];
    NSLog(@"记录请求超时重试列表");
    [self autoStartTimeoutRetry:retryModel];
}

/// 10秒钟之后发起重试请求
/// @param retryModel  重试的Model
- (void)autoStartTimeoutRetry:(NetworkRetryModel *)retryModel {
    
    /*
     *开启一个10秒钟后执行的任务
     *10秒钟后重试
     *如果网络状态变了，这个时候会发送重试的通知
     *所以需要判断是否已经重试过了
     */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"发起请求超时重试");
        if ([self.timeoutRetryTasks containsObject:retryModel]) {
            //防崩溃处理，避免移除崩溃，小概率事件
            @try {
                [self.timeoutRetryTasks removeObject:retryModel];
                [retryModel retryNetwork];
            } @catch (NSException *exception) {
                NSLog(@"请求超时重试发生了异常");
            } @finally {
                
            }
        }
    });
}

/// 取消请求任务
/// @param enginePoint 引擎内存地址
- (void)cancelEngineNetworkTaskWithPoint:(NSString *)enginePoint {
    if (enginePoint) {
        if ([self.engineTasks.allKeys containsObject:enginePoint]) {
            NSURLSessionTask *task = [self.engineTasks valueForKey:enginePoint];
            [task cancel];
            [self.engineTasks removeObjectForKey:enginePoint];
            [self.completeTask setValue:[NSNumber numberWithBool:NO] forKey:enginePoint];
        }
    }
}

/// 移除请求任务->用于网络请求成功之后自动移除
/// @param enginePoint 引擎内存地址
/// @param isSuccess 接口是否成功
- (void)removeEngineNetworkTaskWithPoint:(NSString *)enginePoint isSuccess:(BOOL)isSuccess {
    if (enginePoint) {
        [self.engineTasks removeObjectForKey:enginePoint];
        [self.completeTask setValue:[NSNumber numberWithBool:isSuccess] forKey:enginePoint];
        NSLog(@"请求完成:%@,记录被移除",enginePoint);
    }
}

/// 查询接口是否正在请求
/// @param method 查询的接口方法名
/// @param isWait 是否等待接口完成才回调
/// @param completeBlock 结果
- (void)searchNetwork:(NSString *)method waitUntilNetworkComplete:(BOOL)isWait methodCompleteBlock:(void(^)(BOOL isRunning, NetworkTaskState taskState))completeBlock {
    //申请临时变量存储，防止一边遍历一边添加数据
    NSDictionary<NSString *, NSURLSessionDataTask *> *tempEngineTask = [NSDictionary dictionaryWithDictionary:self.engineTasks];
    for (NSString *engineKey in [tempEngineTask.allKeys reverseObjectEnumerator]) {
        if ([engineKey containsString:method]) {
            if (isWait) {
                [self waitingNetworkCompleteWithEngineKey:engineKey completeBlock:completeBlock];
                return;
            }else{
                if (completeBlock) {
                    completeBlock(YES,NetworkTaskStatePending);
                }
                return;
            }
        }
    }
    //申请临时变量存储，防止一边遍历一边添加数据
    NSDictionary<NSString *, NSNumber *> *tempCompleteTask = [NSDictionary dictionaryWithDictionary:self.completeTask];
    for (NSString *engineKey in [tempCompleteTask.allKeys reverseObjectEnumerator]) {
        if ([engineKey containsString:method]) {
            if (completeBlock) {
                completeBlock(YES,[[tempCompleteTask valueForKey:engineKey] boolValue]);
            }
            return;
        }
    }
    if (completeBlock) {
        completeBlock(NO,NetworkTaskStateNone);
    }
}

/// 等待接口都请求完成    
/// @param completeBlock 完成回调 (0:失败 1:成功 3:没请求？)
/// @param method 接口列表
- (void)waiteNetworkSuccessWithBlock:(void(^)(NSDictionary<NSString *, NSNumber *> *result))completeBlock methodKeys:(nonnull NSString *)method, ... {
    if (method) {
        va_list args;
        va_start(args, method);
        NSMutableDictionary<NSString *, NSNumber *> *requestResult = [[NSMutableDictionary alloc] init];
        NSString *obj = method;
        do {
            [requestResult setValue:@(-1) forKey:obj];
            BOOL isFind = NO;
            //申请临时变量存储，防止一边遍历一边添加数据
            NSDictionary<NSString *, NSURLSessionDataTask *> *tempEngineTask = [NSDictionary dictionaryWithDictionary:self.engineTasks];
            for (NSString *engineKey in [tempEngineTask.allKeys reverseObjectEnumerator]) {
                if ([engineKey containsString:obj]) {
                    [self waitingNetworkCompleteWithEngineKey:engineKey completeBlock:^(BOOL isRunning, NetworkTaskState taskState) {
                        [requestResult setValue:@(taskState) forKey:obj];
                    }];
                    isFind = YES;
                    break;
                }
            }
            if (!isFind) {
                //申请临时变量存储，防止一边遍历一边添加数据
                NSDictionary<NSString *, NSNumber *> *tempCompleteTask = [NSDictionary dictionaryWithDictionary:self.completeTask];
                for (NSString *engineKey in [tempCompleteTask.allKeys reverseObjectEnumerator]) {
                    if ([engineKey containsString:obj]) {
                        [requestResult setValue:[tempCompleteTask valueForKey:engineKey] forKey:obj];
                        isFind = YES;
                        break;
                    }
                }
            }
            //异常处理，没有找到这个请求的时候
            if (!isFind) {
                [requestResult setValue:@(3) forKey:obj];
            }
        } while((obj = va_arg(args, NSString *)));
        va_end(args);
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            while (true && requestResult.allKeys.count > 0) {
                if (![requestResult.allValues containsObject:@(-1)]) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (completeBlock) completeBlock(requestResult);
                    });
                    break;
                }
            }
        });
    }else {
        if (completeBlock) completeBlock(nil);
    }
}

/// 等待指定的网络请求完成
/// @param engineKey 引擎Key
/// @param completeBlock 完成回调
- (void)waitingNetworkCompleteWithEngineKey:(NSString *)engineKey completeBlock:(void(^)(BOOL isRunning, NetworkTaskState taskState))completeBlock {
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (true) {
            if ([weakSelf.completeTask.allKeys containsObject:engineKey]) {
                if (completeBlock) {
                    completeBlock(NO,[[weakSelf.completeTask valueForKey:engineKey] boolValue]);
                }
                break;
            }
        }
    });
}

#pragma mark -- Getter, Setter --

- (NSMutableDictionary<NSString *,NSURLSessionDataTask *> *)engineTasks {
    if (!_engineTasks) {
        _engineTasks = [[NSMutableDictionary alloc] init];
    }
    return _engineTasks;
}

- (NSMutableDictionary<NSString *, NSNumber *> *)completeTask {
    if (!_completeTask) {
        _completeTask = [[NSMutableDictionary alloc] init];
    }
    return _completeTask;
}

- (NSMutableArray<NetworkRetryModel *> *)retryTasks {
    if (!_retryTasks) {
        _retryTasks = [[NSMutableArray alloc] init];
    }
    return _retryTasks;
}

- (NSMutableArray<NetworkRetryModel *> *)timeoutRetryTasks {
    if (!_timeoutRetryTasks) {
        _timeoutRetryTasks = [[NSMutableArray alloc] init];
    }
    return _timeoutRetryTasks;
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
